[AWS IoT Core] カスタム認証を使用してHTTPSでPublishしてみました
1 はじめに
IoT事業部の平内(SIN)です。
AWS IoT Coreへのアクセスでは、カスタム認証によって、独自の認証認可を構築することができますが、今回は、HTTPSを使用して、カスタム認証でメッセージブローカーにメッセージをパブリッシュしてみました。
下記は、IoT Coreでサポートされているプロトコルの一覧ですが、このうちの9番目のものとなります。
Protocols, authentication, and port mappings
No. | Protocol | Operations supported | Authentication | Port | ALPN protocol name |
---|---|---|---|---|---|
1 | MQTT over WebSocket | Publish, Subscribe | Signature Version 4 | 443 | N/A |
2 | MQTT over WebSocket | Publish, Subscribe | Custom authentication | 443 | N/A |
3 | MQTT | Publish, Subscribe | X.509 client certificate | 443 | x-amzn-mqtt-ca |
4 | MQTT | Publish, Subscribe | X.509 client certificate | 8883 | N/A |
5 | MQTT | Publish, Subscribe | Custom authentication | 443 | mqtt |
6 | HTTPS | Publish only | Signature Version 4 | 443 | N/A |
7 | HTTPS | Publish only | X.509 client certificate | 443 | x-amzn-http-ca |
8 | HTTPS | Publish only | X.509 client certificate | 8443 | N/A |
9 | HTTPS | Publish only | Custom authentication | 443 | N/A |
参考:How to Use Your Own Identity and Access Management Systems to Control Access to AWS IoT Resources
2 Lambda
HTTPSでアクセスした場合、下記のようなリクエストがLambdaに到着します。
{ "protocolData": { "tls": { "serverName": "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com" }, "http": { "headers": { "x-amz-customauthorizer-signature": "xxxxxxxxxxxx", "content-length": "22", "host": "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com", "user-agent": "my-agent", "x-amz-customauthorizer-name": "custom_auth_2021_08_21", "content-type": "application/x-www-form-urlencoded", "accept": "*/*", "my-token": "sample-token" }, "queryString": "" } }, "protocols": [ "tls", "http" ], "token": "sample-token", "signatureVerified": True, "connectionMetadata": { "id": "5c72112f-3510-5f81-42e5-d43acc09a1ac" } }
そして、Lambdaから返すのは、以下の内容になります。
- IsAuthenticated: 認証の有効無効を返すブール値
- PrincipalId: 識別子(1〜128文字)
- PolicyDocuments: JSON形式のポリシー(各ポリシーは、2,048文字以内で、10個まで指定可能)
- DisconnectAfterInSeconds: AWS IoT ゲートウェイへの接続期間(300〜86,400秒)
- RefreshAfterInSeconds: ポリシーをキャッシュする期間(300〜86,400秒)
オーソライザーで署名を有効としている場合、署名の検証に成功した場合(signatureVerifiedは、Trueとなっています)だけ、Lambdaがコールされるので、ここでは、下記のようなコードで、トピック(my-topic)にPublishできる権限を返すことにします。
custom_auth_http_2021_08_21
import json policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iot:Publish", "Resource": "arn:aws:iot:ap-northeast-1:*:topic/my-topic" } ] } def lambda_handler(event, context): print("eventy: {}".format(json.dumps(event))) is_authenticated = False if "signatureVerified" in event: is_authenticated = event["signatureVerified"] principal_id = "CustomAuthId" disconnect = 84000 refresh = 300 return { "isAuthenticated": is_authenticated, "principalId": principal_id, "disconnectAfterInSeconds": disconnect, "refreshAfterInSeconds": refresh, "policyDocuments": [ policy ] }
3 秘密鍵・公開鍵の作成
署名が有効なオーソライザを作成するためには、公開鍵の登録が必要です。
下記では、opensslを使用して、秘密鍵(id_rsa)と公開鍵(id_rsa.pub)を作成しています。
% openssl genrsa -out id_rsa 4096 % openssl rsa -in id_rsa -pubout -out id_rsa.pub % cat id_rsa.pub -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxHb3PH7Mmfufj8pKhX78 ・・・略・・・ e6iQM2IL41oq1bTk8McSG2ECAwEAAQ== -----END PUBLIC KEY-----
4 オーソライザー
IoT Coreのコンソールから安全性 - オーソライザーと辿り、新規にオーソライザーを作成します。 設定した内容は、以下の通りです
- オーソライザーの名前: custom_auth_2021_08_21としました
- オーソライザー関数: custom_auth_http_2021_08_21 上記で作成したもの
- トークン署名を有効化: チェック(デフォルト値)
- トークンヘッダ名: my-token としました
- キー名: device-001としました
- 値: 上記で作成した公開鍵
- オーソライザーアプティブ: チェック
「オーソライザーの名前」「トークンヘッダ名」は、アクセス時に必要なので、名前を控えておきます。「オーソライザーアプティブ」は、デフォルトで、チェックされていませんが、オーソライザー自体を有効とするためにチェッックを忘れないように注意が必要です。
IoT Coreのコンソールからオーソライザーを作成すると、設定したLambdaに自動的にリソースベースのポリシーが追加されます。
5 動作確認
動作確認のために、トークンに署名を行います。トークンは、何でも構いませんが、ここでは、sample-tokenとしました。
% echo -n "sample-token" | openssl dgst -sha256 -sign id_rsa | openssl base64 -A
curlでアクセスする場合、以下のようになります。
curl -X POST -k -i -N -H "Host: <endpoint>" \ -H "X-Amz-CustomAuthorizer-Name: <authorizer_name>" \ -H "X-Amz-CustomAuthorizer-Signature: <token_signature>" \ -H "<token_key_name>: sample-token" \ -H "User-Agent: my-agent" \ --data 'Hello from custom auth' https://<endpoint>/topics/my-topic
- endpooint IoT Coreのエンドポイントです
% aws iot describe-endpoint --endpoint-type iot:Data-ATS
- authorizer_name オーソライザーの名前です。(custom_auth_2021_08_21)
- token_signature 署名したシグネチャ openssl dgstで出力したもの
- token_key_name オーソライザーの設定で指定した「トークンヘッダ名」(my-token)
アクセスに成功すると、StatusCode 200が返されます。
% curl -X POST -k -i -N -H "Host: xxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com" -H "X-Amz-CustomAuthorizer-Name: custom_auth_2021_08_21" -H "X-Amz-CustomAuthorizer-Signature: xxxx" -H "my-token: sample-token" -H "User-Agent: my-agent" --data 'Hello from custom auth' https://xxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com/topics/my-topic HTTP/1.1 200 OK content-type: application/json content-length: 65 date: Sat, 21 Aug 2021 00:44:31 GMT x-amzn-RequestId: 5f5376f1-243c-d8a6-00e9-26aa376ab2cd connection: keep-alive
テストクライアントで、到着しているメッセージが確認できます。
6 最後に
今回は、HTTPSを使用して、カスタム認証でメッセージブローカーにメッセージをパブリッシュしてみました。
この仕組みを利用すると、HTTPSからのメッセージ送信は、「認証情報」や「X.509クライアント証明書」が必要なくなります。 送信ヘッダを自由にプログラムできる場合は、一つの手段として有力かもしれません。
7 参考リンク
AWS IoT Enhanced Custom Authorizer Demo
Connecting to AWS IoT Core by using custom authentication
[AWS IoT Core] カスタム認証を使用してHTTPSでPublishしてみました
[AWS IoT Core] カスタム認証(ユーザー名・パスワード)を使用してMQTTでPublish/Subscribeしてみました
[AWS IoT Core] カスタム認証を使用してMQTT over WebSocketでPublish/Subscribeしてみました